package com.bizmda.bizsip.app.client;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.bizmda.bizsip.common.*;
import com.bizmda.bizsip.config.AbstractSinkConfig;
import com.bizmda.bizsip.config.RabbitmqSinkConfig;
import com.bizmda.bizsip.config.RestSinkConfig;
import com.bizmda.bizsip.config.SinkConfigMapping;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Method;

@Slf4j
public class SinkClientMethod {
    private final Method method;
    private final SinkClientProxy sinkClientProxy;

    private RestTemplate restTemplate;
    private SinkConfigMapping sinkConfigMapping;
    private RabbitTemplate rabbitTemplate;

    public SinkClientMethod(Method method, SinkClientProxy sinkClientProxy) {
        this.method = method;
        this.sinkClientProxy = sinkClientProxy;
        if (this.sinkConfigMapping == null) {
            this.sinkConfigMapping = SpringUtil.getBean(SinkConfigMapping.class);
        }
        if (this.restTemplate == null) {
            this.restTemplate = SpringUtil.getBean(RestTemplate.class);
        }
        if (this.rabbitTemplate == null) {
            this.rabbitTemplate = SpringUtil.getBean(RabbitTemplate.class);
        }
    }

    public Object execute(Object[] args) throws BizException {
//        final Annotation[] annotations = method.getAnnotations();
//        for (Annotation annotation : annotations) {
//            Class<? extends Annotation> aClass = annotation.annotationType();
////            if (aClass.equals(Insert.class)) {
////                System.out.println("execute insert {" + args[0] + "} completed");
////            }
////            if (aClass.equals(Update.class)) {
////                System.out.println("execute update {" + args[0] + "} completed");
////            }
//        }
        BizMessage<JSONObject> outMessage = this.callSink(this.sinkClientProxy.getSinkId(), args);
        if (this.sinkClientProxy.getMapperInterface().equals(BizMessageInterface.class)) {
            return outMessage;
        }
        JSONObject jsonObject = new JSONObject(outMessage.getData());
        Object result = jsonObject.get("result");
        return BizUtils.convertMethodReturnJson2Bean(method,result);
//        if (result == null) {
//            return null;
//        }
//        if (result instanceof JSONObject) {
//            return JSONUtil.toBean((JSONObject) result,method.getReturnType());
//        }
//        else if (result instanceof JSONArray) {
//            if (method.getReturnType() == List.class) {
//                return Convert.convert(method.getReturnType(),result);
//            }
//            JSONArray jsonArray = (JSONArray) result;
//            return jsonArray.toArray(method.getReturnType());
//        }
//        else {
//            return Convert.convert(method.getReturnType(),result);
//        }
    }

    private BizMessage<JSONObject> callSink(String sinkId, Object[] args) throws BizException {
        JSONObject jsonObject = new JSONObject();
        if (this.sinkClientProxy.getMapperInterface().equals(BizMessageInterface.class)) {
            if (args[0] instanceof JSONObject) {
                jsonObject = (JSONObject) args[0];
            }
            else {
                jsonObject = JSONUtil.parseObj(args[0]);
            }
        }
        else {
            jsonObject.set("className", sinkClientProxy.getMapperInterface().getName());
            jsonObject.set("methodName", this.method.getName());
            jsonObject.set("params", JSONUtil.parseArray(args));
            JSONObject parametersTypes = BizUtils.getParamtersTypesJsonObject(this.method,args);
            if (parametersTypes.size() > 0) {
                jsonObject.set("paramsTypes",parametersTypes);
            }
        }
        log.trace("调用Sink接口参数:\n{}",BizUtils.buildJsonLog(jsonObject));
        BizMessage<JSONObject> inMessage = BizUtils.bizMessageThreadLocal.get();
        inMessage.setData(jsonObject);

        AbstractSinkConfig sinkConfig = (AbstractSinkConfig) sinkConfigMapping.getSinkConfig(sinkId);
        BizMessage<JSONObject> outMessage = null;
        if ("rest".equalsIgnoreCase(sinkConfig.getType())) {
            RestSinkConfig restServerAdaptorConfig = (RestSinkConfig)sinkConfig;
            log.debug("调用RESTful服务:{}",restServerAdaptorConfig.getUrl());
            outMessage = this.restTemplate.postForObject(restServerAdaptorConfig.getUrl(), inMessage, BizMessage.class);
            if (outMessage.getCode() != 0) {
                throw new BizException(outMessage);
            }
        }
        else if ("rabbitmq".equalsIgnoreCase(sinkConfig.getType())) {
            RabbitmqSinkConfig rabbitmqSinkConfig = (RabbitmqSinkConfig) sinkConfig;
            log.debug("调用RabbitMQ服务:rpc-mode[{}],exchange[{}],route-key-{}]",
                    rabbitmqSinkConfig.isRpcMode(),rabbitmqSinkConfig.getExchange(), rabbitmqSinkConfig.getRoutingKey());
            if (rabbitmqSinkConfig.isRpcMode()) {
                CorrelationData correlationData = new CorrelationData(inMessage.getTraceId());
                outMessage = (BizMessage<JSONObject>) this.rabbitTemplate
                        .convertSendAndReceive(rabbitmqSinkConfig.getExchange(),
                                rabbitmqSinkConfig.getRoutingKey(), inMessage, correlationData);
                if (outMessage.getCode() != 0) {
                    throw new BizException(outMessage);
                }
            }
            else {
                this.rabbitTemplate.convertAndSend(rabbitmqSinkConfig.getExchange(),
                        rabbitmqSinkConfig.getRoutingKey(),inMessage);
                inMessage.setData(new JSONObject());
                return inMessage;
            }
        }
        else {
            throw new BizException(BizResultEnum.OTHER_ERROR,"未知的Sink类型:"+sinkConfig.getType());
        }
        BizUtils.debug("返回",outMessage);
        if (!(outMessage.getData() instanceof JSONObject)) {
            outMessage.setData(JSONUtil.parseObj(outMessage.getData()));
        }
        return outMessage;
    }

}
